import fs from "fs";
import { Page, ElementHandle, Browser } from "puppeteer";
import { sleep } from "./baseUtils.js";
import puppeteer from "puppeteer-extra";
import StealthPlugin from "puppeteer-extra-plugin-stealth";

class PuppeteerUtils {
  /*
    // 1.拟人化爬虫
    实现泛模拟真人操作的爬虫类
    实现html节点分析函数
    实现智能回退函数
    实现智能点击函数
    实现页面滑动函数(快滑和慢滑)
    // 2.自动化网页检测向功能
    重点：实现设置下载路径和浏览器路径配置函数
    实现更便捷的断点调试功能
    实现浏览器的性能检测和错误日志上报
    实现浏览器页面的基本自动化bug检测功能    
    */
}

// 这是我的谷歌浏览器本地路径
const chromePath = "C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe";

// 下载路径，浏览器路径，是否隐藏浏览器,窗口大小等等
export async function launchBrowser(
  downloadPath: string,
  headless: boolean = false,
  windowSize: { width: number; height: number } = { width: 1920, height: 1080 }
) {
  puppeteer.use(StealthPlugin());

  const browser = await puppeteer.launch({
    executablePath: chromePath,
    headless,
    args: [
      "--no-sandbox",
      "--disable-setuid-sandbox",
      "--disable-dev-shm-usage",
      "--disable-accelerated-2d-canvas",
      "--no-first-run",
      "--no-zygote",
      "--disable-gpu",
    ],
    defaultViewport: windowSize,
  });
  const page = await browser.newPage();
  return { browser, page };
}
// 路径设置函数：拟人化设置最重要的函数之一
export async function setDownloadPath(page: Page, downloadPath: string) {
  // 检查并创建下载路径
  if (!fs.existsSync(downloadPath)) {
    // 创建目录
    fs.mkdirSync(downloadPath, { recursive: true });
  }
  // 设置下载路径
  const client = await page.createCDPSession();
  await client.send("Page.setDownloadBehavior", {
    behavior: "allow",
    downloadPath,
  });
}

// 点击函数
// 传入选择器，点击元素
async function clickElementBySelector(page: Page, selector: string) {
  try {
    // 等待选择器加载
    await page.waitForSelector(selector, { visible: true });

    // 点击该选择器的元素
    await page.click(selector);
    console.log(`成功点击选择器: ${selector}`);
    return page;
  } catch (error) {
    console.error(`无法点击选择器: ${selector}`, error);
  }
}
// 传入元素类型和文本，点击元素
async function clickElementByTextAndType(
  page: Page,
  elementType: string,
  text: string
) {
  try {
    // 构造 XPath 表达式来查找指定类型的元素
    const xpath = `//${elementType}[contains(text(), '${text}')]`;

    await page.waitForFunction(
      (xpath) => {
        return (
          document.evaluate(
            xpath,
            document,
            null,
            XPathResult.FIRST_ORDERED_NODE_TYPE,
            null
          ).singleNodeValue !== null
        );
      },
      {},
      xpath,
      { timeout: 5000 }
    );

    // 使用 page.evaluateHandle 执行 XPath 查询
    const elementHandle = await page.evaluateHandle((xpath) => {
      const result = document.evaluate(
        xpath,
        document,
        null,
        XPathResult.FIRST_ORDERED_NODE_TYPE,
        null
      );
      return result.singleNodeValue;
    }, xpath);

    // 检查元素是否存在并点击
    const validElementHandle: any = elementHandle.asElement();
    if (validElementHandle) {
      await validElementHandle.click(); // 点击找到的元素
      console.log(`成功点击类型为 ${elementType} 且包含文本: ${text}`);
      return page;
    } else {
      console.error(`未找到类型为 ${elementType} 且包含文本: ${text} 的元素`);
    }
  } catch (error) {
    console.error(`无法点击类型为 ${elementType} 且包含文本: ${text}`, error);
  }
}
// 传入图片元素的alt或者Src
async function clickImageByAltOrSrc(
  page: Page,
  altText: string,
  srcText: string
) {
  try {
    // 构造 XPath 表达式来查找指定 alt 或 src 的 img 元素
    let xpath = "//img";
    if (altText) {
      xpath += `[contains(@alt, '${altText}')]`;
    } else if (srcText) {
      xpath += `[contains(@src, '${srcText}')]`;
    }

    // 等待 img 元素可见
    await page.waitForFunction(
      (xpath) => {
        return (
          document.evaluate(
            xpath,
            document,
            null,
            XPathResult.FIRST_ORDERED_NODE_TYPE,
            null
          ).singleNodeValue !== null
        );
      },
      {},
      xpath,
      { timeout: 5000 } // 设置合适的超时时间，视页面加载速度而定
    );

    // 使用 page.evaluateHandle 执行 XPath 查询找到图像的元素句柄
    const elementHandle = await page.evaluateHandle((xpath) => {
      const result = document.evaluate(
        xpath,
        document,
        null,
        XPathResult.FIRST_ORDERED_NODE_TYPE,
        null
      );
      return result.singleNodeValue;
    }, xpath);

    // 检查元素是否存在并点击
    const validElementHandle: any = elementHandle.asElement();
    if (validElementHandle) {
      await validElementHandle.click(); // 点击找到的图像元素
      console.log(`成功点击图片，匹配 alt: ${altText} 或 src: ${srcText}`);
      return page;
    } else {
      console.error(`未找到匹配 alt: ${altText} 或 src: ${srcText} 的图片`);
    }
  } catch (error) {
    console.error(
      `无法点击匹配 alt: ${altText} 或 src: ${srcText} 的图片`,
      error
    );
  }
}

// 搜集所有带有特定选择器的图片标签的 alt 属性并点击
async function clickAllImages(page: Page, selector: string, browser: Browser) {
  const newPagePromise = waitForNewPage(browser);
  const imageAlts = await page.$$eval("img.card-image", (images) =>
    images.map((img) => img.getAttribute("alt") || "")
  );

  for (const [index, altText] of imageAlts.entries()) {
    if (!altText) continue; // 如果 altText 是空的，跳过该元素
    try {
      await clickImageByAltOrSrc(page, altText, "");
      const newPage = await newPagePromise;
      await sleep(100, 1000);
      // 关闭新开的页面
      await newPage.close();

      console.log(`找到图片了，alt: ${altText}`);
    } catch (error) {
      console.error(
        `Failed to process image ${index + 1} with alt "${altText}"`,
        error
      );
    }
  }
}

// 输入函数
async function inputText(page: Page, selector: string, text: string) {
  try {
    // 等待选择器加载
    await page.waitForSelector(selector, { visible: true });

    // 输入文本
    await page.type(selector, text);
    console.log(`成功输入文本: ${text} 到选择器: ${selector}`);
    return page;
  } catch (error) {
    console.error(`无法输入文本: ${text} 到选择器: ${selector}`, error);
  }
}

// 监听新导航页面
async function waitForNewPage(browser: Browser): Promise<Page> {
  return new Promise((resolve, reject) => {
    browser.once("targetcreated", async (target) => {
      if (target.type() !== "page") return;
      try {
        const newPage = await target.page();
        if (!newPage) {
          return reject(new Error("新页面未能成功创建"));
        }
        await newPage.waitForNavigation();
        resolve(newPage);
      } catch (error) {
        reject(new Error("新页面初始化过程中出现错误"));
      }
    });
  });
}


// 等待页面刷新
async function waitForNavigation(page: Page): Promise<Page> {
  try {
    // 监听页面的导航事件。
    await page.waitForNavigation({ waitUntil: "load" });
    console.log("页面导航完成");
    return page;
  } catch (error) {
    console.error("等待导航过程中出现错误:", error);
    throw error; // 重新抛出错误以便调用者处理
  }
}

// 监听下载的 PDF 文件
async function waitForPdfDownload(page: Page) {
  return new Promise<void>((resolve, reject) => {
    page.on("response", async (response) => {
      const url = response.url();
      if (url.endsWith(".pdf")) {
        console.log(`Detected PDF download: ${url}`);
        try {
          await sleep(100, 3000); // 等待 3 秒
          console.log("Resolving after 3 seconds delay.");
          resolve();
        } catch (error) {
          reject(new Error("PDF 处理过程出错"));
        }
      }
    });
  });
}

export {
  clickElementBySelector as clickElement,
  clickElementByTextAndType as clickText,
  clickImageByAltOrSrc as clickImage,
  inputText,
  clickAllImages,
  waitForPdfDownload,
  waitForNavigation,
  waitForNewPage
};
